package threadpoolnio.server;

import threadpoolnio.threadpool.PoolHelper;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ExecutorService;

/**
 * @author ：xx
 * @date ：Created in 2020/3/12 20:09
 * @description：
 * @modified By：
 */
public class NIOServer2 extends Thread {
    final int TIMEOUT = 3000;
    final int PORT = 9999;
    final int BUF_SIZE = 1024;
    Selector selector = null;
    ServerSocketChannel ssc = null;
    private boolean isContine = true;
    private ExecutorService threadPool=null;
    //使用Map保存每个连接，当OP_READ就绪时，根据key找到对应的文件对其进行写入
    Map<SelectionKey, FileChannel> fileMap = new HashMap<SelectionKey, FileChannel>();
    Map<SelectionKey, Long> timeMap=new HashMap<>();
    public static void main(String[] args) {
//        selector();
    }

    @Override
    public void run() {
        System.out.println("=============服务器已启动================");
        threadPool=PoolHelper.getPool();
        selector();
    }

    public void selector() {
        try {
//        第一步：创建Selector
            selector = Selector.open();
//        第二步：创建ServerSocketChannel，绑定监听端口
            ssc = ServerSocketChannel.open();
            ssc.socket().bind(new InetSocketAddress(PORT));
//        第三步：将Channel设置为非阻塞模式
            ssc.configureBlocking(false);
//        第四步：将Channel注册到Selector上，监听连接事件
            ssc.register(selector, SelectionKey.OP_ACCEPT);
//        第五步：循环调用Selector的select方法，检测就绪情况
            while (isContine) {
                if (selector.select(TIMEOUT) == 0) {
//                    System.out.println("===等待连接===");
                    continue;
                }
                //  第六步：调用selectedKeys方法获取就绪channel集合
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    //   第七步：判断就绪事件种类，调用业务处理方法
                    if (key.isValid() && key.isAcceptable()) {
//                        System.out.println("===========连接===========");
                        handleAccept(key);
                    }
                    if (key.isValid() && key.isConnectable()) {
                        System.out.println("isConnectable = true");
                    }
                    if (key.isValid() && key.isReadable()) {
                        handleRead(key);
                    }
                    if (key.isValid() && key.isWritable()) {
//                        System.out.println("===========写===========");
                        handleWrite(key);
                    }
                    iterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void handleAccept(SelectionKey key) throws IOException {
        ServerSocketChannel ssChannel = (ServerSocketChannel) key.channel();
        SocketChannel sc = ssChannel.accept();
        System.out.println("===========" + sc.getRemoteAddress() + "连接===========");
        sc.configureBlocking(false);
        sc.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocateDirect(BUF_SIZE));
    }

    public void handleRead(SelectionKey key) throws IOException {
        // 移除掉这个key的可读事件，放入线程池里面处理
        //&~  就是  取消 一个 read 事件
        //| 就是 更新一个 read 的事件
        key.interestOps(key.interestOps() & (~SelectionKey.OP_READ));
        ReaderThread readerThread=new ReaderThread(key,fileMap,timeMap);
        threadPool.execute(readerThread);

    }


    public void handleWrite(SelectionKey key) throws IOException {
        ByteBuffer buf = (ByteBuffer) key.attachment();
        buf.flip();
        SocketChannel sc = (SocketChannel) key.channel();
        while (buf.hasRemaining()) {
            sc.write(buf);
        }
        buf.compact();
    }

    public void close() {
        try {
            isContine = false;
            Thread.sleep(1000);
            if (selector != null) {
                selector.close();
            }
            if (ssc != null) {
                ssc.close();
            }
            System.out.println("===========服务器已停止===================");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
